#include "functionality.h"


int client; //client socket - because we do the fork we need only a single variable for the socket
void doAction(char* request);
void sendMsg(int* data);
/*Array that keeps all tickets*/
int ticketsAll[6]; //should be shared memory - for reading
/*Array that keeps reserved tickets*/
int ticketsReserved[6]; //send via FIFO to server
/*for automatic zombie kill*/
time_t begin, end;
void resetTimer();

/*lockers and update checkers*/
void doLock();
static void lockHandler(int);
void unlock();
static void unlockHandler(int);
void updateNotify();
static void updateHandler(int);

/*For data synchronization*/
int fifo;
bool locked;

main()
    {

    /*INIT FIELDS*/
    int sock;
    struct  sockaddr_un servaddr; /* address of server */
    struct  sockaddr_un from; /* address of client */
    int fromlen;
    pid_t proc, clientWrapper;

    /*Set up address structure for server socket */
    servaddr.sun_family = AF_UNIX;
    strcpy(servaddr.sun_path, SOCKET_PATH);
    unlink(SOCKET_PATH);

    /*Configure data syncronizers*/
    mkfifo("data.file", 0700);
    fifo = open("data.file", O_RDWR);
    locked = false;

    /*Install handlers*/
    if(signal(SIGCHLD, lockHandler)==SIG_ERR)
        {
        fprintf(stderr, "Lock: Cannot set signal handler\n");
        exit(0);
        }
    if(signal(SIGUSR1, unlockHandler)==SIG_ERR)
        {
        fprintf(stderr, "Unlock: Cannot set signal handler\n");
        exit(0);
        }
    if(signal(SIGUSR2, updateHandler)==SIG_ERR)
        {
        fprintf(stderr, "Update: Cannot set signal handler\n");
        exit(0);
        }

    /*Initialize the array of tickets*/
    ticketsAll[0] = 0; //redundant field, just to make client-server synchronization easier
    ticketsAll[1] = 1000;
    ticketsAll[2] = 1000;
    ticketsAll[3] = 1000;
    ticketsAll[4] = 1000;
    ticketsAll[5] = 1000;

    /*Try to create a UNIX stream socket for server*/
    if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        {
        perror("Server: socket");
        exit(1);
        }

    if (bind(sock, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
        {
        close(sock);
        perror("Server: bind");
        exit(1);
        }

    if (listen(sock, 50)<0)
        {
        perror("Server: listen");
        exit(1);
        }

    unlock();
    while(1)
        {
        if((client = accept(sock, (struct sockaddr *) &from, &fromlen))<0)
            {
            perror("Server: accept");
            exit(1);
            }
        else if (client != 0)
            {
            proc = fork();
            if(proc==0) //Child wrapper on a server side
                {
                /*Zombie avoidance -> pass client to init process*/
                clientWrapper = fork();
                if (clientWrapper>0)
                    {
                    exit(0);
                    }
                /*Zombie avoidance: end*/
                int rlen;
                char buffer[50];
                clearArray(ticketsReserved); //clear it in child (once!)
                resetTimer();
                while(1)
                    {
                    if(timeOut()) //self termination
                        {
                        printf("%d Self termination\n", client);
                        close(client);
                        kill(getpid(), SIGKILL);
                        }
                    clearArray(buffer); //clear the array: remove unknown characters
                    rlen = read(client, &buffer, MESSAGE_STR_SIZE);
                    if (rlen > 0) //read operation succeeds
                        {
                        resetTimer();
                        /*ACTUAL LOGIC START*/
                        doAction(buffer);
                        /*ACTUAL LOGIC END*/
                        }
                    else if(rlen==0)
                        {
                        //printf("Server: 0 bytes read");
                        }
                    else
                        {
                        perror("Server: read");
                        }
                    }
                close(client);
                }
            wait(NULL);//does not block - child was immidiately terminated
            }
        }

    unlink(SOCKET_PATH);
    close(sock);
    printf("Server done\n");
    exit(0);
    }

void doAction(char* request)
    {
    int data[6];
    char buffer[MESSAGE_STR_SIZE];
    deserialize(request, data);
    switch(data[0])//what to do when a certain request comes
        {
        case LIST_GET:
            printf("%d Obtain the list\n", client);
            //clearArray(data);
            ticketsAll[0] = LIST_SEND;
            sendMsg(ticketsAll);
            break;
        case RESERVATION_DO:
            //while(isLocked()){;}//wait until all other process performs modification
            doLock();
            printf("%d Reserve\n", client);
            printf("%s", request);
            //Global update operation
            /*read(fifo, &buffer, MESSAGE_STR_SIZE);
            deserialize(buffer, ticketsAll);
            user should not be able to reserve more tickets than it is available
            if(ticketsAll[1]<ticketsReserved[1] || ticketsAll[2]<ticketsReserved[2] || ticketsAll[3]<ticketsReserved[3] || ticketsAll[4]<ticketsReserved[4] || ticketsAll[5]<ticketsReserved[5])
                {
                unlock(); //if reservation is not possible unlock data as soon as possible
                clearArray(data);
                data[0] = RESERVATION_FAIL;
                data[1] = RESERVATION_FAIL_FAULT;
                sendMsg(data);
                break;
                }
            //If everything is OK
            ticketsAll[0] = data[0];
            ticketsAll[1] -= data[1];
            ticketsAll[2] -= data[2];
            ticketsAll[3] -= data[3];
            ticketsAll[4] -= data[4];
            ticketsAll[5] -= data[5];
            serialize(ticketsAll, buffer);
            write(fifo, buffer, MESSAGE_STR_SIZE);*/
            unlock();//*/
            ticketsReserved[1] += data[1];
            ticketsReserved[2] += data[2];
            ticketsReserved[3] += data[3];
            ticketsReserved[4] += data[4];
            ticketsReserved[5] += data[5];
            ticketsAll[0] = RESERVATION_OK;
            ticketsAll[1] -= data[1];
            ticketsAll[2] -= data[2];
            ticketsAll[3] -= data[3];
            ticketsAll[4] -= data[4];
            ticketsAll[5] -= data[5];
            clearArray(data);
            copyArray(ticketsAll, data);
            showTickets(ticketsAll, ticketsReserved);
            sendMsg(data);
            break;
        case CANCEL_DO:
            printf("%d Cancel\n", client);
            ticketsAll[0] = CANCEL_OK;
            ticketsAll[1] += ticketsReserved[1];
            ticketsAll[2] += ticketsReserved[2];
            ticketsAll[3] += ticketsReserved[3];
            ticketsAll[4] += ticketsReserved[4];
            ticketsAll[5] += ticketsReserved[5];
            clearArray(data);
            clearArray(ticketsReserved);
            copyArray(ticketsAll, data);
            showTickets(ticketsAll, ticketsReserved);
            sendMsg(data);
            break;
        case BARGIN_DO:
            printf("%d Buy\n", client);
            clearArray(data);
            clearArray(ticketsReserved);
            data[0] = BARGIN_OK;
            sendMsg(data);
            break;
        case EXIT:
            printf("%d Exit\n", client);
            ticketsAll[1] += ticketsReserved[1];
            ticketsAll[2] += ticketsReserved[2];
            ticketsAll[3] += ticketsReserved[3];
            ticketsAll[4] += ticketsReserved[4];
            ticketsAll[5] += ticketsReserved[5];
            //kill(getpid(), SIGKILL);
            close(client);
            exit(0);
            break;
        default:
            printf("Unspecified message code\n");
        }
    }

void sendMsg(int* data)
    {
    char response[MESSAGE_STR_SIZE];
    clearArray(response);
    serialize(data, response);
    write(client, response, strlen(response));
    }

int timeOut()
    {
    int interval;
    (void) time(&end);
    interval = (int) end - begin;
    if(interval<TIME_TO_DIE)
        {
        return 0;
        }
    else
        {
        printf("Time-out: %d seconds.\n", interval);
        return 1;
        }
    }

void resetTimer()
    {
    printf("Timer reset done.\n");
    (void) time(&begin);
    (void) time(&end);
    }

/*lockers and update checkers*/
void doLock()
    {
    kill(0, SIGCHLD);
    }
static void lockHandler(int sig_no)
    {
    locked = true;
    printf("%d: lock\n", client);
    }

void unlock()
    {
    kill(0, SIGUSR1);
    }
static void unlockHandler(int sig_no)
    {
    locked = false;
    printf("%d: unlock\n", client);
    }

void updateNotify()
    {
    kill(0, SIGUSR2);
    }
static void updateHandler(int sig_no)
    {
    char response[MESSAGE_STR_SIZE];
    printf("%d: updated\n", client);
    clearArray(response);
    read(fifo, &response, MESSAGE_STR_SIZE);
    write(client, response, strlen(response));
    }

